package com.sqoq.downloader3

import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.text.TextUtils
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.google.gson.reflect.TypeToken
import com.sqoq.downloader3.bean_ddg.VideoListBean_ddg
import com.sqoq.downloader3.retrofit.gson.GsonUtil
import com.tencent.mmkv.MMKV
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.nio.file.Files
import java.util.concurrent.TimeUnit


object DownloadTool_ddg {

    val okHttpClient: OkHttpClient by lazy {
        OkHttpClient.Builder().apply {

            connectTimeout(5, TimeUnit.SECONDS)
            readTimeout(10, TimeUnit.SECONDS)
            writeTimeout(10, TimeUnit.SECONDS)
        }.build()
    }


    fun download(
        lifecycleOwner: LifecycleOwner,
        url: String,
        hot: Boolean,
        onDownloadSuccess: (VideoListBean_ddg) -> Unit,
        onProgress: (Int) -> Unit,
        onDownloadFailed: (Exception) -> Unit,
        title: String,
    ): Call? {
//var path2 = "https://dl.tiktokio.com/download?token=atHsRx0cccHM6Ly9hcGkudGlrdG9rdi5jb20vYXdlbWUvdjEvcGxheS8oo00odmlkZW9faWQ9djA5MDI1ZzQwMDAwY252MHVqdm9nNjV1dmphZHU1NzAmbGluZT0wJmlzX3BsYXlfdXJsPTEmc291cmNlPVBhY2tTb3VyY2VFbnVtX0ZFRUQmZmlsZV9pZD02NjBlOGFjNzE4ZjI0YWE2OTQ4OWViNWJhOTg1MmNjNCZpdGVtX2lkPTczNDkzMzAyMTM4ODY1OTQzMDkmc2lnbmF0dXJldjM9ZG1sa1pXOWZhV1E3Wm1sc1pWOXBaRHRwZEdWdFgybGtMakpoWmpVelltWXdZVEkwTTJReVpXSTVOemsyWkdZNFl6QTNNemsyWTJWbAO0O0OO0O0O&p=dtGslxrcdcG9raW8uY29t"
//        var path2 = "https://sunap.oss-us-west-1.aliyuncs.com/%E4%B8%8B%E8%BD%BD.mp4"

        kotlin.runCatching {
            val request: Request = Request.Builder().get().url(url).build()
            val call: Call = okHttpClient.newCall(request)

            call.enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
                        onDownloadFailed.invoke(e)
                    }
                }

                override fun onResponse(call: Call, response: Response) {
                    response.body?.let {
                        val inputStream: InputStream = response.body!!.byteStream()
                        var parentPath = ""
                        if (Build.VERSION.SDK_INT in 30..32) {
                            parentPath = FileUtilsl_ddg.createRootPath(App.app)
                        } else {
                            parentPath =
                                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath + "/downloadVideo";
                        }
                        if (File(parentPath).exists().not()) {
                            File(parentPath).mkdirs()
                        }
                        var filename = getHeaderFileName(response)
                        if (filename.isNullOrEmpty()) {
                            filename = System.currentTimeMillis().toString() + ".mp4"
                        }

                        val target = File(parentPath, filename)
                        if (target.exists()) {
                            File(parentPath).delete()
                        }
                        val total = response.body!!.contentLength()
                        val fileOutputStream = FileOutputStream(target)
                        try {
                            val buffer = ByteArray(2048)
                            var len: Int
                            var sum: Long = 0
                            var mprogress = -1
                            var time = System.currentTimeMillis()
                            while (inputStream.read(buffer).also { len = it } != -1) {
                                fileOutputStream.write(buffer, 0, len)
                                sum += len;
                                val progress = (sum * 1.0f / total * 100).toInt()
                                if (mprogress != progress) {
                                    mprogress = progress
                                    if (System.currentTimeMillis() - time > 100) {
                                        time = System.currentTimeMillis()

                                        lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
                                            onProgress.invoke(progress)
                                        }
                                    }
                                }
                            }
                            fileOutputStream.flush()
                            if (Build.VERSION.SDK_INT in 30..32) {
                                var isSave = saveVideoToAlbumAfterQ(App.app, target.path)
                                var filePath =
                                    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).absolutePath + File.separator + App.app.packageName + File.separator + target.name
                                kotlin.runCatching {
                                    target.delete()
                                }
                                lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
                                    if (isSave) {

                                        onDownloadSuccess.invoke(save(url, filePath, hot, title))
                                    } else {
                                        onDownloadFailed.invoke(java.lang.Exception(""))
                                    }
                                }
                            } else {
                                lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
                                    MediaScannerConnection.scanFile(
                                        App.app,
                                        arrayOf(target.path),
                                        null,
                                        object : MediaScannerConnection.OnScanCompletedListener {
                                            override fun onScanCompleted(path: String?, uri: Uri?) {

                                            }
                                        });

                                    onDownloadSuccess.invoke(save(url, target.path, hot, title))
                                }
                            }
                        } catch (e: Exception) {
                            lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
                                onDownloadFailed.invoke(e)
                            }

                        } finally {
                            kotlin.runCatching {
                                fileOutputStream.close()
                            }
                            kotlin.runCatching {
                                inputStream.close()
                            }

                        }
                    }
                }
            })
            return call
        }
        lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
            onDownloadFailed.invoke(java.lang.Exception(""))
        }

        return null
    }

    private fun saveVideoToAlbumAfterQ(context: Context, videoFile: String): Boolean {
        var uri: Uri? = null
        return try {
            val contentUri: Uri
            contentUri =
                if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
                    MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                } else {
                    MediaStore.Video.Media.INTERNAL_CONTENT_URI
                }
            val contentResolver = context.contentResolver
            val file = File(videoFile)
            val contentValues = getVideoContentValues(context, file, System.currentTimeMillis())
                ?: return true
            uri = contentResolver.insert(contentUri, contentValues)
            if (copyFileAfterQ(context, contentResolver, file, uri)) {
                contentValues.clear()
                contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0)
                context.contentResolver.update(uri!!, contentValues, null, null)
                context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri))
                true
            } else {
                context.contentResolver.delete(uri!!, null, null)
                false
            }
        } catch (e: Exception) {
            if (uri != null) {
                context.contentResolver.delete(uri, null, null)
            }
            e.printStackTrace()
            false
        }
    }

    /**
     * 获取视频的contentValue
     */
    private fun getVideoContentValues(
        context: Context,
        paramFile: File,
        timestamp: Long
    ): ContentValues? {
        val localContentValues = ContentValues()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            localContentValues.put(
                MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES
                        + File.separator + context.packageName
            )
        }
        val contentUri: Uri
        contentUri =
            if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI
            } else {
                MediaStore.Video.Media.INTERNAL_CONTENT_URI
            }
        val cursor = context.contentResolver.query(
            contentUri,
            arrayOf(
                MediaStore.Video.Media.DATA,
                MediaStore.Video.Media.DISPLAY_NAME,
                MediaStore.Video.Media.RELATIVE_PATH
            ),
            MediaStore.Video.Media.DISPLAY_NAME + "='" + paramFile.name + "'",
            null,
            null
        )
        while (cursor != null && cursor.moveToNext()) {
            val path =
                cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.RELATIVE_PATH))
            if (path.contains(
                    Environment.DIRECTORY_MOVIES
                            + File.separator + context.packageName
                )
            ) {
                return null
            }
        }
        localContentValues.put(MediaStore.Video.Media.TITLE, paramFile.name)
        localContentValues.put(MediaStore.Video.Media.DISPLAY_NAME, paramFile.name)
        localContentValues.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
        localContentValues.put(MediaStore.Video.Media.DATE_TAKEN, timestamp)
        localContentValues.put(MediaStore.Video.Media.DATE_MODIFIED, timestamp)
        localContentValues.put(MediaStore.Video.Media.DATE_ADDED, timestamp)
        localContentValues.put(MediaStore.Video.Media.SIZE, paramFile.length())
        return localContentValues
    }

    @Throws(IOException::class)
    private fun copyFileAfterQ(
        context: Context,
        localContentResolver: ContentResolver,
        tempFile: File,
        localUri: Uri?
    ): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
            context.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q
        ) {
            //拷贝文件到相册的uri,android10及以上得这么干，否则不会显示。可以参考ScreenMediaRecorder的save方法
            var os: OutputStream? = null
            try {
                os = localContentResolver.openOutputStream(localUri!!)
                Files.copy(tempFile.toPath(), os)
                return true
            } catch (e: Exception) {
            } finally {
                if (os != null) {
                    try {
                        os.close()
                    } catch (e: Exception) {
                    }
                }
            }
        }
        return false
    }

    private fun getHeaderFileName(response: Response): String? {
        var dispositionHeader = response.header("Content-Disposition")
        if (!TextUtils.isEmpty(dispositionHeader)) {
            dispositionHeader!!.replace("attachment;filename=", "")
            dispositionHeader.replace("filename*=utf-8", "")
            val strings =
                dispositionHeader.split("; ".toRegex()).dropLastWhile { it.isEmpty() }
                    .toTypedArray()
            if (strings.size > 1) {
                dispositionHeader = strings[1].replace("filename=", "")
                dispositionHeader = dispositionHeader.replace("\"", "")
                return dispositionHeader
            }
            return ""
        }
        return ""
    }

    fun getDownLoadData(): List<VideoListBean_ddg> {
        return GsonUtil.gson().fromJson<ArrayList<VideoListBean_ddg>>(
            MMKV.defaultMMKV().getString("videoList", "[]"),
            object : TypeToken<ArrayList<VideoListBean_ddg>>() {}.type
        ).filter { File(it.filePath_ddg).exists() }.reversed()
    }

    fun getDownLoadData2(): MutableList<VideoListBean_ddg> {
        var data2 = getDownLoadData()
        kotlin.runCatching {
            if (data2.isNullOrEmpty().not()) {
                var uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                var cursor = App.app.contentResolver.query(
                    uri,
                    null,
                    null,
                    null,
                    MediaStore.Video.Media.DATE_MODIFIED + " desc"
                )
                cursor!!.moveToFirst()

                val pathColumn = cursor!!.getColumnIndex(MediaStore.Video.Media.DATA)

                val DURATION_Column = cursor!!.getColumnIndex(MediaStore.Video.Media.DURATION)
                do {
                    kotlin.runCatching {
                        val filePath = cursor!!.getString(pathColumn!!)
                        data2.forEach {
                            if (it.filePath_ddg == filePath) {
                                it.duration = cursor!!.getLong(DURATION_Column!!)
                            }
                        }
                    }
                } while (cursor!!.moveToNext())
            }
        }
        return data2.toMutableList()
    }

    fun getDownLoadPath(url: String): VideoListBean_ddg? {
        var data = getDownLoadData().filter { it.url_ddg.equals(url) }
        if (data.isNullOrEmpty()) {
            return null;
        }
        return data.get(0)
    }

    fun delete(data: VideoListBean_ddg) {
        var list = getDownLoadData()
        list = list.filter { it.filePath_ddg != data.filePath_ddg }
        kotlin.runCatching {
            if (File(data.filePath_ddg).exists()) {
                File(data.filePath_ddg).delete()
            }
        }
        MMKV.defaultMMKV().encode("videoList", GsonUtil.gson().toJson(list))
    }

    fun save(url: String, filePath: String, hot: Boolean, title: String): VideoListBean_ddg {
        var data = getDownLoadData().toMutableList()
        var dss = VideoListBean_ddg(
            url, filePath, hot, title
        )
        data.add(
            dss
        )
        MMKV.defaultMMKV().encode("videoList", GsonUtil.gson().toJson(data))
        return dss
//        RxBus.get().post(RefreshDownLoad_ddg())

    }
}